FINAL FANTASY III (NES) SOUND ENGINE FORMAT v1.0 By Justin Olbrantz (Quantam) The permanent home of this specification and others, with the latest updates, is https://github.com/TheRealQuantam/RetroDocs. The Final Fantasy III sound engine, like that of Final Fantasy II, was developed by Hiroshi Nakamura. However, both engines, as well as that of the original Final Fantasy developed by Toshiaki Imai, all show a clear evolution of features and music format over time; Final Fantasy II builds on Final Fantasy, and III then builds on II. The FF3 engine is not entirely abstracted from the NES' Audio Processing Unit hardware. In particular, while it is unclear whether this was intended for music or only sound effects, FF3's engine allows direct access to the pitch slide register which provides pitch linear pitch slide for square wave channels but can potentially interfere with pitch envelopes. The details of this will be discussed under the corresponding command. For further information see a proper APU programming reference such as https://www.nesdev.org/wiki/APU_registers Basic Features: - Compact single-byte notes and rests + Staff notation style divisive rhythm system - 6-octave chromatic scale from C2 (65.4 Hz) to B7 (7457 Hz) for square wave (triangle is 1 octave lower) - 16 note lengths from whole notes through 64th notes with dots and triplets for some lengths - Length tie to create arbitrary lengths - High-precision tempo from 1 to 255 BPM + Complex timbre support Square channels: Duty cycle, channel volume, pitch linear pitch slide, ASR volume envelope, and linear period pitch envelope Triangle channel: Linear period pitch envelope Noise channel: Channel volume, ASR volume envelope, and linear period pitch envelope - 5 channels - square 1 and 2, triangle, noise, and a kick drum-like pop - each with separate event sequences - Note auto-release - Dual-level nested loops, with loop break points possible for "play twice" loops - Nearly identical formats for music and sound effects Major Differences with Final Fantasy: - 2 additional octaves - Proper tempo system - Proper implementation of rests and tied length - Note auto-release - Dual-level nested loops with break points - Support for noise and kick drum channels + Drastically improved expression - Selection of square wave duty cycles - Channel volume selection for square and noise channels - ASR volume envelopes and linear period pitch envelopes - Looping envelopes - Nearly identical formats for music and sound effects Major Differences with Final Fantasy II: - No explicit access to ctrl 1 registers ($4000, $4004, etc.) - Access to pitch linear pitch slide registers ($4001, $4005, etc.) for square wave channels - Volume envelopes are partially required - ASR volume envelopes on square wave and noise channels - Note auto-release - Dual-level nested loops - Nearly identical formats for music and sound effects Some general notes in interpreting this specification: - All word (16-bit) values in the format are little-endian, with the least-significant byte preceding the most-significant. - All values are unsigned unless noted otherwise. - All numbers preceded by $ are hex, and in general unless they represent byte strings or addresses most numbers without $ are in decimal. - Byte values are generally shown as hex "ab" where "a" is the high nibble and "b" is the low nibble (standard mathematical digit ordering). However, when it is necessary to show bit fields, bytes are shown in binary as "abcd efgh" (mathematical order); in bit fields "-" means that the value of the bit does not matter in the given context. - Many command lists (e.g. channel data commands) are encoded ambiguously, where 1 value could be interpreted as multiple different commands (e.g. 00 could match 0n). These lists are ordered such that the first matching command is the correct interpretation. - A wave's "period" is inversely proportionate to its frequency; in the NES the square wave channel's period = 1789773/16 / frequency (triangle channel is 1 octave lower for the same period), and the value actually written to the hardware is 1 less than this. Here "pitch" is used as proportionate to key number, is what humans perceive, and frequency doubles (period halves) with each pitch increase of 1 octave. As such "linear period", "linear frequency", and "linear pitch" are all very different things, and linear pitch is what sounds linear to the ear. MUSIC FORMAT FF3's sound system stores all its code, global data, and sound effects in bank $36 in the $8000-$9fff region. Music data as well as track tables are distributed across multiple banks that are mapped to $a000-$bfff as necessary. To convert addresses to file offsets: For addresses $8000-$9fff: file offset = addr + $64010 For addresses $a000-$bfff: file offset = bank * $2000 + addr - $9ff0 Music Header Structure: +0 word[5]: Channel data addresses in order of square 1, square 2, triangle, noise, kick drum, or $ffff if none. Channel Data Format Notes and rests are encoded as single-byte events of the format kL. For notes, k is 0-$b, corresponding to C-B within the current octave, and L is the length the note should play before continuing. Key number: 0 1 2 3 4 5 6 7 8 9 $a $b Key: C C# D D# E F F# G G# A A# B For the noise channel the keys are mapped to noise values according to the following table. For noise values, like period values but unlike the keys that map to them, lower values mean higher pitch. Key number: 0 1 2 3 4 5 6 7 8 9 $a $b Octave 0: 0 1 2 3 4 5 6 7 8 9 $a $b Octave 1: $c $e $f $f $f $f $f $f $f $f $f $f Octave 2: $80 $81 $82 $83 $84 $85 $86 $87 $88 $89 $8a $8b Octave 3: $8c $8e $8f $8f $8f $8f $8f $8f $8f $8f $8f $8f Octaves 2 and 3 are periodic noise, a buzzing/ringing sound that approximates certain chromatic tones (although consistently about 50 cents sharp) as shown in the following table: Key number: 0 1 2 3 4 5 6 7 8 9 $a $b Octave 2: D8 D7 D6 D5 D4 G3 D3 A#2 F#2 D2 G1 D1 Octave 3: G0 D-1 D-2 D-2 D-2 D-2 D-2 D-2 D-2 D-2 D-2 D-2 Octaves 4 and 5 are not useful in the general case, but are used in combination with volume and volume envelopes by the presets for closed hihat and snare drum: Key number: 0 1 2 3 4 5 6 7 8 9 $a $b Octave 4: 4 4 4 4 4 4 4 4 4 4 4 4 (closed hihat) Octave 5: $b $b $b $b $b $b $b $b $b $b $b $b (snare drum) Key is irrelevant for the kick drum channel. Note events may be immediately followed (i.e. no other events may come between them) by 1 or more tied lengths of the form dL that serve to lengthen the initial note by L time. Finally, rests have the form cL. Unlike in FF1, rests in FF3 behave as expected and properly silence the preceding note. In all cases L is a length code from the following table: whole half 4th 8th 16th 32nd 64th Normal note lengths: 0 2 5 8 $b $d Dotted note lengths: 1 3 6 9 Triplet note lengths: 4 7 $a $c $e $f However, the specified length is the length until the next event is played, and is not necessarily the length the note will actually play before being silenced, due to auto-release. When starting notes FF3 sets the sustain length of notes to a hard-coded 3/4 the total length of the note including tied length. What exactly this means depends on the volume envelope used. If a volume envelope is used the sustain length is the maximum time the note may be in the sustain phase of its envelope before being automatically transitioned to release phase. However, this only means anything if that envelope has a sustain phase and its release phase fades out. Some (e.g. percussion) envelopes effectively do not have a sustain phase and will fade out even without auto-release; other (e.g. tremolo) envelopes do not have release envelopes that fade out, and the note will continue to play until a new note is started or a rest is encountered. Rests immediately silence the preceding note even if its envelope has not yet brought the volume to 0. Once again auto-release has no effect on the kick drum channel. In addition to notes, rests, and tied lengths, the following commands are defined: e0 tt: Set global tempo to t BPM. Default of 150. e1-ee: Set channel volume to 2 ($e1)-15 ($ee) (default of 15). Note that channel volume is ONLY applied if there is also a volume envelope. ef-f4: Set octave 0 ($ef)-5($f4). Octave 0 begins at C2, resulting in a total key span of C2-B7 for square wave channels; triangle is 1 octave lower. f5-f7 vv pp: Set timbre and envelopes. By default no envelopes are used: Square: Set duty cycle to 12.5% ($f5), 25% ($f6), or 50% ($f7), volume envelope number to v ($ff if none), and pitch envelope number to p ($ff if none). Defaults to 12.5% duty cycle. Triangle: Set pitch envelope number to p ($ff if none). Noise: Set volume envelope number to v ($ff if none) and pitch envelope number to p ($ff if none). Kick drum: No effect. f8 eppp nsss: Set pitch slide register for square wave channels ($4001, $4005, etc.). Has no effect on other channels. Defaults to pitch slide disabled. e: Enable pitch slide p: Pitch slide period. Pitch is modified every p+1 half-frames. n: Negative flag. If set, the square wave period is reduced (rising pitch), else the period is increased (falling pitch). s: Pitch slide amount. The amount of change to the period is relative to the current period, resulting in linear pitch slides according to the following table (note that it will be slightly detuned because of how the period registers works): 0: 100% change: if n silences channel after 1 period, else decreases pitch by 1 octave/period 1: 50% change: if n +1 octave/period, else -7.02 semitones/period 2: 25% change: if n +4.98 semitones/period, else -3.86 semitones/period 3: 12.5% change: if n +2.31 semitones/period, else -2.04 semitones/period 4: 6.25% change: if n +1.12 semitones/period, else -1.05 semitones/period 5: 3.12% change: if n +54.96 cents/period, else -53.27 cents/period 6: 1.56% change: if n +27.26 cents/period, else -26.84 cents/period 7: 0.78% change: if n +13.58 cents/period, else -13.47 cents/period Note: The absolute range of the square wave channels is 54.6 Hz (~A1) to 12.4 KHz (~G9), and any channel that is taken outside that range by pitch slide will be silenced. The pitch slide unit ALWAYS computes the next period value and checks it for valid range, but the period value is only updated if pitch slide is enabled; n should be set whenever e is not set to avoid this effect. f9: Closed hihat preset for noise channel. Set octave to 4, volume envelope number to 0, and channel volume to 8 fa: Snare drum preset for noise channel. Set octave to 5, volume envelope number to 1, and channel volume to 15 fb nn: Begin loop and set to play n > 0 times. May have 2 levels of loops. fc xx yy: End loop. Decrement inner loop counter and if not 0 go to y:x, else end inner loop and continue. fd xx yy: Odd loop break. If loop counter is odd (1, 3, etc.) end inner loop and go to y:x, else continue. fe xx yy: Go to y:x ff: End of channel. Stop and silence the channel but other channels will continue. When all channels have ended playback is stopped. Examples Square Wave Channel: E0 91: Set global tempo to $91 (145) BPM F6 09 FF: Set duty cycle to 25% and set envelopes v = 9: Volume envelope number 9 p = $ff: No pitch envelope EB: Set channel volume to $c (12) EF: Set octave 0 (starting at C2) CC: Rest L = $c: Triplet 16th note 58 D0: Play note with tied length k = 5: Key F L = 8, 0: 8th note + whole note F8 8D: Set pitch slide e = 1: Enable pitch slide p = 0: Period length of 0+1=1 half-frames n = 1: Falling period (rising pitch) s = 5: 3.12% change: +54.96 cents/period FB 02: Begin loop n = 2: Play loop sequence 2 times (set loop counter to 2) FD CB A9: Odd loop break y:x = $a9cb: If loop counter is odd (second playthrough of the loop sequence) end loop and go to $a9cb FC B1 A9: End loop y:x = $a9b1: Decrement loop counter; if not 0 go to $a9b1, else end loop and continue Noise Channel: F9: Set closed hihat preset 0B: Play note k = 0: Noise value 4 (all keys are the same with this preset) L = $b: 16th note FA: Set snare drum preset 08: Play note k = 0: Noise value $b (with this preset) L = 8: 8th note FE 63 AA: Goto y:x = $aa63: Address $aa63 Kick Drum Channel: 02: Play note k = 0: Irrelevant L = 2: Half note ENVELOPES The FF3 engine has a relatively complex envelope system capable of ASR and AD envelopes (or any subset thereof) and tremolo for volume envelopes, as well as pitch envelopes and vibrato. Pitch Envelopes Pitch envelopes consist of a single phase and are formed by a table of period offset:length pairs that add an offset to the current period and wait for the specified number of frames before moving onto the next pair, optionally concluding with a loop back to a previous pair. It is important to note that pitch envelopes directly modify the period for the channel, and these modifications accumulate; i.e. envelope values are relative. But the FF3 engine does not allow pitch envelopes to cause the period to cross a $100-value boundary. While this is done to prevent audible glitches caused by writes to the period high register, the direct and cumulative modification of the period combine to create the unwanted side effect that the base pitch can be shifted due to clamping at the high/low points of the envelope. E.g. if a vibrato envelope has the repeating sequence +2, -2, -2, +2 (+/- 2) but the current period is $1ff and +2 would cause a boundary crossing, the result will be $1ff + 2 = $1ff, and then $1ff - 4 + 2 = $1fd, NOT $1ff; the center frequency has shifted. Pitch Envelope Format: 00: End of envelope. No further modification will be made to pitch. 0LLL LLLL pp: Add offset p (signed, two's complement) to current period and wait L frames 1ooo oooo: Loop. Add 1:o (< 0) to current envelope position in bytes. Pitch envelope 1 serves as an example: 32 00 06 FF 06 01 FC 32 00: Offset:length pair L = $32: $32 frames p = 0: +0 period units 06 FF: Offset:length pair L = 6: 6 frames p = $ff: -1 period units 06 01: Offset:length pair L = 6: 6 frames p = 1: +1 period units FC: Loop. $fc = -4 bytes, so go back 2 offset:length pairs This is a delayed vibrato envelope that waits 50 frames before beginning a vibrato of depth 1 period value and period of 12 frames (5 Hz). This small of a depth will produce a very slight but perceivable vibrato, e.g. for A440 +/- about 3.3 cents. 04 FF FE 04 FF: Offset:length pair L = 4: 4 frames p = $ff: -1 period units FE: Loop. $fe = -2 bytes, so go back 1 offset:length pair Envelope 8 demonstrates a couple things. It is a pitch slide envelope, decreasing the period by 1 every 4 frames (rising pitch). This may well not work as intended, however, because of the inability for pitch slide to cause period values to cross $100 boundaries. As such the pitch slide will abruptly end at the next (lowest) $100 boundary. Pitch Envelope Table: 36:9eab word[$10]: Addresses of pitch envelopes Pitch Envelopes: Pairs show period offset on the left side and length on the right. Loops are indicated with structures like -2->4 which show the relative byte offset on the left side and the new absolute offset on the right (2x the number of offset:length pairs). Terminal 00 bytes are not shown for non-looping envelopes. 0 (0) @9ecb (6dedb): -1:4 +1:4 -4->0 1 (1) @9ed0 (6dee0): +0:50 -1:6 +1:6 -4->2 2 (2) @9ed7 (6dee7): -1:50 +1:6 -1:6 -4->2 3 (3) @9ede (6deee): -1:6 +1:6 -4->0 4 (4) @9ee3 (6def3): -1:1 5 (5) @9ee6 (6def6): +0:16 -1:6 +1:6 -4->2 6 (6) @9eed (6defd): +0:6 -1:6 +1:6 -4->2 7 (7) @9ef4 (6df04): +0:24 -1:6 +1:6 -4->2 8 (8) @9efb (6df0b): -1:4 -2->0 9 (9) @9efe (6df0e): -1:24 -1:6 +1:6 -4->2 10 (a) @9f05 (6df15): -1:1 -2:1 -2:1 -2:1 +2:1 +2:1 +2:1 +2:1 +2:1 +2:1 +2:1 -2:1 -2:1 -2:1 -2:1 -28->2 11 (b) @9f24 (6df34): +0:1 -1:1 +1:1 +1:1 -1:1 -8->2 12 (c) @9f2f (6df3f): +7:1 -2:1 -2:1 -2:1 -2:1 -2:1 -2:1 -2:1 -2:1 +1:1 +1:1 +1:1 -1:1 -1:1 -1:1 +1:1 +1:1 +1:1 -1:1 -1:1 -1:1 +1:1 +1:1 +1:1 -1:1 -1:1 -1:1 +2:1 +2:1 +2:1 +2:1 +2:1 +2:1 +2:1 -2:1 -2:1 -2:1 13 (d) @9f7a (6df8a): +1:1 +2:1 +2:1 +2:1 +2:2 -2:1 -2:1 -2:1 -1:1 -18->0 14 (e) @9f8d (6df9d): +7:1 -3:1 -3:1 -3:1 -3:1 -2:2 +1:2 +1:2 -1:2 -1:2 -8->12 15 (f) @9fa2 (6dfb2): -7:1 +7:1 +7:2 -2:1 -3:1 -3:1 -3:1 -3:1 +2:1 +2:1 +2:1 -2:1 -2:1 -2:1 +2:1 +2:1 +2:1 -2:1 -2:1 -2:1 +2:1 +2:1 +2:1 -2:1 -2:1 -2:1 +2:1 +2:1 +2:1 +2:1 +2:1 +2:1 +2:1 Code to load the address of a pitch envelope: 86DE LDA $9EAB,Y 86E1 STA $D8 86E3 LDA $9EAC,Y 86E6 STA $D9 Volume Envelopes Volume envelopes are significantly more complex and offer greater flexibility, consisting of 3 phases: attack, sustain, and release. However, any of these phases may be 0-length, and they need not be used as named; e.g. the attack or sustain envelopes can be used as decay instead, for non-sustaining instruments. Unlike pitch envelopes, channels have an envelope volume value separate from the channel volume specified by the corresponding channel command; these 2 values are multiplied to get the final volume which is actually written to the hardware. Note auto-release is directly related to the volume envelope. When a note begins, the specified length of the note and any tied lengths are added up, and 3/4 of this value (subject to some rounding errors) is assigned as the note's sustain length. The sustain length determines the maximum time the note may stay in the sustain phase before it is forced into the release phase, if the envelope does not result in the phase ending sooner. This sustain length is exclusive to the sustain phase, and does not include the time spent in the attack phase; this makes it strongly recommended that harmonies between channels should share an envelope if there is an attack phase. The attack phase is the simplest. It consists of a simple table of 1 or more volume values, 1 per frame; 1 value is always necessary to set the initial volume of the sustain and release phases. Each frame the corresponding value is assigned to the channel's envelope volume, and the attack phase ends when the table ends. While the intent of the attack phase is to go from pre-note silence to max volume, many FF3 envelopes use it as a decay envelope, fading out from max volume either to silence or to some lower sustained volume, and do not use sustain or release envelopes at all. Sustain and release phases function almost identically and in fact share almost all of their code. They are notably more complex than the attack phase, for several reasons. The basic structure of sustain/release envelope tables is identical to that of the pitch envelope: a series of offset:length pairs with an optional repeat point, which tells the engine to set the volume and then wait for a number of frames before modifying it again. However, there is a key difference in interpretation due to the fact that the purpose of a volume envelope is typically to fade out; in fact the primary mechanism of the sustain and release phases is a per-phase parameter that specifies fade-out speed between 0 (no fade out) and 100 (decrement the envelope volume each frame). This is how the envelope volume value is used, and when it reaches 0 the note is stopped, whether in sustain or release phase. The envelope table in fact serves as an adjustment value to this process; rather than actually modifying the envelope volume value, each frame the channel volume is multiplied by the envelope volume + the current adjustment value to get the value written to the hardware. As such the current table values do NOT accumulate, nor do they affect the end of note condition caused by the envelope volume value reaching 0. Conversely, the phase does not end when the table ends; rather, the fade out continues with no further modification from the table (i.e. the modification value is 0). To summarize: Attack Phase: - Simple, per-frame table-based - Table must contain at least 1 entry to serve as initial envelope volume - Phase ends when the table ends Sustain Phase: - Initial volume specified by the final volume of the attack phase - Advances to the release phase when auto-release occurs (when the note's sustain length expires) Both Sustain and Release Phases: - Persistent fade out + table-based modification value - Table can loop - The end of the table removes the volume modification step from the process but does not end the phase - The note ends when the fade out reaches 0 volume regardless of the modification value Release Phase: - Only reachable via auto-release - Initial volume specified by the final (unmodified) fade-out value of the sustain phase Attack Envelope Format: 0v: Envelope volume v ff (1--- ----): End of phase Anything else: INVALID Sustain/Release Envelope Header: +0 byte: Volume fade-out/frame * 100, from 0 to 100. If 0, volume is never decremented. Sustain/Release Envelope Format: 00: End of modification table. Fade-out continues as long as note is in phase. 0LLL LLLL vv: Use volume offset v (signed, two's complement) for the next L frames 1ooo oooo: Loop. Add 1:o (< 0) to current envelope position in bytes. This offers considerable flexibility and potential for space-savings, though some examples may be needed to fully understand. Volume Envelope 0 (0): A @9ad4 (6dae4): 15 9 3 0 Envelope 0 has no sustain or release phases, and consists only of an attack envelope that serves as a rapid decay, and is used for the closed hihat preset among others. This is necessary because sustain/release phases cannot fade out faster than 1 volume per frame. Volume Envelope 16 (10): A @9c46 (6dc56): 15 S @9c48 (6dc58): 100 R @9c48 (6dc58): 100 Envelope 16 features a more gradual fade out. The attack phase simply sets the initial volume for the fade out, and the sustain and release phases have no modification tables but specify a fade out of 100: 1 volume per frame. Volume Envelope 23 (17): A @9cab (6dcbb): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Envelope 23 is a pure attack envelope that ramps up to 15 and maintains max volume until the next note or rest. Volume Envelope 37 (25): A @9e75 (6de85): 15 S @9e77 (6de87): 0 -15:1 +0:1 -4->1 R @9e77 (6de87): 0 -15:1 +0:1 -4->1 Envelope 37 is an extreme tremolo (perhaps even frequency modulation) envelope. The alternating volume modifications each frame create a tremolo with a depth of 15 and period of 2 frames (30 Hz) that continues indefinitely (remember that the modification value causing the volume to be 0 does not cause the phase to end). Volume Envelope Table: 36:9a7c struct[$29] +0 word: Address of attack envelope +2 word: Address of sustain envelope +4 word: Address of release envelope Volume Envelopes For sustain/release envelopes pairs show volume offset on the left side and length on the right. Loops are indicated with structures like -2->4 which show the relative byte offset on the left side and the new absolute offset on the right (2x the number of offset:length pairs). Terminal bytes are not shown for non-looping envelope tables. Volume Envelope 0 (0): A @9ad4 (6dae4): 15 9 3 0 Volume Envelope 1 (1): A @9ae1 (6daf1): 15 12 9 7 5 3 2 1 0 Volume Envelope 2 (2): A @9af3 (6db03): 15 15 14 14 13 13 12 12 11 11 10 10 9 9 8 8 8 7 7 7 7 6 S @9b0a (6db1a): 20 R @9b0a (6db1a): 20 Volume Envelope 3 (3): A @9af3 (6db03): 15 15 14 14 13 13 12 12 11 11 10 10 9 9 8 8 8 7 7 7 7 6 S @9b14 (6db24): 10 R @9b14 (6db24): 10 Volume Envelope 4 (4): A @9b5b (6db6b): 5 6 7 8 9 9 10 10 11 11 12 12 13 13 15 S @9b6b (6db7b): 5 +0:2 -1:2 -4->1 R @9b6b (6db7b): 5 +0:2 -1:2 -4->1 Volume Envelope 5 (5): A @9b89 (6db99): 11 12 13 14 15 14 13 12 11 10 9 8 7 7 6 6 6 5 S @9b9c (6dbac): 10 R @9b9c (6dbac): 10 Volume Envelope 6 (6): A @9b89 (6db99): 11 12 13 14 15 14 13 12 11 10 9 8 7 7 6 6 6 5 S @9ba6 (6dbb6): 5 R @9ba6 (6dbb6): 5 Volume Envelope 7 (7): A @9bb0 (6dbc0): 15 6 8 9 10 11 12 13 14 15 S @9bbb (6dbcb): 5 +0:1 -1:2 -2:2 -1:2 +0:1 -10->1 R @9bbb (6dbcb): 5 +0:1 -1:2 -2:2 -1:2 +0:1 -10->1 Volume Envelope 8 (8): A @9bcf (6dbdf): 5 6 7 8 9 9 10 10 11 11 13 13 15 15 S @9bde (6dbee): 5 +0:1 -1:2 +0:1 -6->1 R @9bde (6dbee): 5 +0:1 -1:2 +0:1 -6->1 Volume Envelope 9 (9): A @9bee (6dbfe): 15 14 13 12 11 10 9 8 8 7 7 7 6 S @9bfc (6dc0c): 25 R @9bfc (6dc0c): 25 Volume Envelope 10 (a): A @9c10 (6dc20): 7 9 11 13 15 S @9c16 (6dc26): 5 +0:2 -1:2 -4->1 R @9c16 (6dc26): 5 +0:2 -1:2 -4->1 Volume Envelope 11 (b): A @9af3 (6db03): 15 15 14 14 13 13 12 12 11 11 10 10 9 9 8 8 8 7 7 7 7 6 S @9b1e (6db2e): 5 R @9b1e (6db2e): 5 Volume Envelope 12 (c): A @9b5b (6db6b): 5 6 7 8 9 9 10 10 11 11 12 12 13 13 15 S @9b79 (6db89): 5 R @9b79 (6db89): 5 Volume Envelope 13 (d): A @9af3 (6db03): 15 15 14 14 13 13 12 12 11 11 10 10 9 9 8 8 8 7 7 7 7 6 S @9b14 (6db24): 10 R @9b14 (6db24): 10 Volume Envelope 14 (e): A @9b5b (6db6b): 5 6 7 8 9 9 10 10 11 11 12 12 13 13 15 S @9b79 (6db89): 5 R @9b79 (6db89): 5 Volume Envelope 15 (f): A @9c2a (6dc3a): 3 3 5 5 7 7 9 9 11 11 13 13 15 S @9c38 (6dc48): 5 +0:2 -1:2 -4->1 R @9c38 (6dc48): 5 +0:2 -1:2 -4->1 Volume Envelope 16 (10): A @9c46 (6dc56): 15 S @9c48 (6dc58): 100 R @9c48 (6dc58): 100 Volume Envelope 17 (11): A @9bee (6dbfe): 15 14 13 12 11 10 9 8 8 7 7 7 6 S @9c06 (6dc16): 5 R @9c06 (6dc16): 5 Volume Envelope 18 (12): A @9c5c (6dc6c): 15 15 14 14 13 13 12 S @9c64 (6dc74): 0 +0:1 -1:2 -2:2 -1:2 +0:1 -10->1 R @9c64 (6dc74): 0 +0:1 -1:2 -2:2 -1:2 +0:1 -10->1 Volume Envelope 19 (13): A @9c78 (6dc88): 15 13 11 9 7 5 4 Volume Envelope 20 (14): A @9c88 (6dc98): 15 12 9 6 3 0 Volume Envelope 21 (15): A @9c97 (6dca7): 15 S @9c99 (6dca9): 33 R @9c99 (6dca9): 33 Volume Envelope 22 (16): A @9ca1 (6dcb1): 15 Volume Envelope 23 (17): A @9cab (6dcbb): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Volume Envelope 24 (18): A @9d40 (6dd50): 1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7 8 8 8 8 9 9 9 9 10 10 10 10 11 11 11 11 12 12 12 12 13 13 13 13 14 14 14 14 15 15 15 15 0 Volume Envelope 25 (19): A @9dcb (6dddb): 1 3 5 7 7 7 7 7 7 7 7 9 11 13 15 15 15 15 15 15 15 15 15 13 11 9 7 7 7 7 7 7 7 7 5 3 1 0 Volume Envelope 26 (1a): A @9e29 (6de39): 3 6 9 12 15 Volume Envelope 27 (1b): A @9e46 (6de56): 15 15 3 3 6 9 12 15 S @9e4f (6de5f): 33 R @9e4f (6de5f): 33 Volume Envelope 28 (1c): A @9cab (6dcbb): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 S @9cdd (6dced): 0 -3:1 -6:1 -9:1 -6:1 -3:1 +0:1 -12->1 R @9cdd (6dced): 0 -3:1 -6:1 -9:1 -6:1 -3:1 +0:1 -12->1 Volume Envelope 29 (1d): A @9c46 (6dc56): 15 S @9c50 (6dc60): 100 -6:1 +0:1 -4->1 R @9c50 (6dc60): 100 -6:1 +0:1 -4->1 Volume Envelope 30 (1e): A @9e57 (6de67): 3 6 9 12 15 S @9e5d (6de6d): 0 -2:1 -4:1 -6:1 -4:1 -2:1 +0:1 -12->1 R @9e5d (6de6d): 0 -2:1 -4:1 -6:1 -4:1 -2:1 +0:1 -12->1 Volume Envelope 31 (1f): A @9d86 (6dd96): 1 0 1 0 2 0 2 0 3 0 3 0 4 0 4 0 5 0 5 0 6 0 6 0 7 0 7 0 8 0 8 0 9 0 9 0 10 0 10 0 11 0 11 0 12 0 12 0 13 0 13 0 14 0 14 0 15 0 15 0 Volume Envelope 32 (20): A @9cf9 (6dd09): 0 2 0 4 0 6 0 8 0 10 0 12 0 14 0 15 S @9d0a (6dd1a): 0 -15:1 +0:1 -4->1 R @9d0a (6dd1a): 0 -15:1 +0:1 -4->1 Volume Envelope 33 (21): A @9d1c (6dd2c): 1 0 3 0 5 0 7 0 9 0 11 0 13 0 15 S @9d2c (6dd3c): 0 -15:1 -15:1 +0:1 -4->3 R @9d2c (6dd3c): 0 -15:1 -15:1 +0:1 -4->3 Volume Envelope 34 (22): A @9cc5 (6dcd5): 8 0 9 0 10 0 11 0 12 0 13 0 14 0 15 S @9cd5 (6dce5): 100 R @9cd5 (6dce5): 100 Volume Envelope 35 (23): A @9cab (6dcbb): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 S @9cf1 (6dd01): 100 R @9cf1 (6dd01): 100 Volume Envelope 36 (24): A @9dfa (6de0a): 1 0 5 0 7 0 7 0 7 0 7 0 11 0 15 0 15 0 15 0 15 0 15 0 11 0 7 0 7 0 7 0 7 0 5 0 1 0 Volume Envelope 37 (25): A @9e75 (6de85): 15 S @9e77 (6de87): 0 -15:1 +0:1 -4->1 R @9e77 (6de87): 0 -15:1 +0:1 -4->1 Volume Envelope 38 (26): A @9e37 (6de47): 3 6 9 12 15 0 Volume Envelope 39 (27): A @9b2e (6db3e): 15 15 14 14 13 13 12 12 11 11 10 10 9 9 8 8 8 7 7 7 6 6 6 6 5 5 5 5 5 4 4 4 4 4 4 3 S @9b53 (6db63): 5 R @9b53 (6db63): 5 Volume Envelope 40 (28): A @9e83 (6de93): 7 7 9 11 13 15 15 15 15 10 5 10 15 10 5 0 5 10 15 10 5 0 0 5 10 15 10 5 0 0 0 5 10 15 10 5 0 Code to load a volume envelope table address: 85BB LDA $9A7C,Y 85BE STA $D6 85C0 LDA $9A7D,Y 85C3 STA $D7 SOUND EFFECT FORMAT The sound effect format is nearly identical to the music format, and is played mostly by the same code. The only 2 noteworthy differences are that sound effects are limited to the square 2 and noise channels and have a fixed tempo of 150 BPM in which 1 tick = 1 frame (see the note/rest length table); in fact sound effects MUST NOT set the tempo, as that will set the tempo for music playback instead. Sound Effect Header Structure: +0 word[2]: Channel data addresses in order of square 2, noise ADDITIONAL TABLES Note/Rest Length Table in Ticks: 0: Whole (96) 1: Dotted half (72) 2: Half (48) 3: Dotted 4th (36) 4: Triplet half (32) 5: 4th (24) 6: Dotted 8th (18) 7: Triplet 4th (16) 8: 8th (12) 9: Dotted 16th (9) a: Triplet 8th (8) b: 16th (6) c: Triplet 16th (4) d: 32nd (3) e: Triplet 32nd (2) f: Triplet 64th (1) Track Tables FF3 distributes both its tracks and its track tables across multiple $2000-byte banks. The bank at address $8000 is $36 for all sound code, while the bank at $a000 and header address are shown below. Note that sometimes the track header or channel data is in the common bank at $8000-$9fff. Track Header Address Tables (array of words): 37:a000: Tracks 0 (0) to 24 (18) 38:a000: Tracks 25 (19) to 42 (2a) 39:8c77: Tracks 43 (2b) to 54 (36) 39:b3ae: Tracks 55 (37) to 58 (3a) 9:b400: Tracks 59 (3b) to 64 (40) Combined Track and Channel Addresses: # Name Bk HdrA Sq1A Sq2A TriA NoiA KckA 0 Resting at the Inn 37:a032: a03c a05f a067 1 The Prelude 37:a07c: a086 a1ca 2 Crystal Cave 37:a1d2: a1dc a324 a401 3 Elia, the Maiden of Water 37:a444: a44e a4a6 a4ae 4 Lute of Noah 37:a56a: a574 a641 5 Return of the Warrior 37:a685: a68f a700 a774 6 Town of Water 37:a7f6: a800 a861 a8bf 7 Fanfare 37:a97c: a986 a9d6 aa1f aa5f aa6e 8 Chocobos! 37:aa81: aa8b ab3d ab81 9 Good Ol' Fellows 37:abef: abf9 ac7e acd8 a Go Above the Clouds! 37:ad36: ad40 ada4 ae40 b Cute Little Tozas 37:aeb9: aec3 af5a afd6 c Jinn, the Fire 37:b042: b04c b0ba b114 d Living Forest 37:b16c: b176 b1fa b25b e Hazardous Short Music 3 37:b2b8: b2c2 b2e9 b30e f Beneath the Horizon 37:b323: b32d b370 b403 10 Time Remains 37:b452: b45c b4b2 b502 11 Vegies of Geasal 37:b58b: b595 b608 b66a 12 In the Covert Town 37:b6a8: b6b2 b77d b879 13 The Requiem 37:b92a: b934 b9b2 ba35 14 Opening Theme 37:ba6d: ba77 bb0c bbad 15 Deep Under the Water 37:bc54: bc5e bcb5 bd60 16 Shrine of Nept 37:be23: be2d be9f becd 17 Item Get 37:bf3d: bf47 bf63 bf74 18 Garuda Defeat 37:bf81: bf8b bfb8 bfdc 19 Big Chocobo! 38:a024: a02e a090 a0e5 1a Swift Twist 38:a125: a12f a175 a1ab 1b Good Morning! 38:a1d2: a1dc a23a a268 1c Dancer's Dance 38:a2a7: a2b1 a2ed a31e 1d The Dungeon 38:a332: a33c a37c a3e3 1e Eternal Wind 38:a46f: a479 a4f9 a63d 1f My Home Town 38:a748: a752 a7cd a827 20 Battle 38:a92b: a935 a9bf aae1 aba6 ac38 21 The Way to the Top 38:ac7f: ac89 acde ad5b 22 Sailing Enterprise 38:ad97: ada1 ae05 ae5d 23 The Invincible 38:af00: af0a af8b b02e 24 Tower of Owen 38:b0a5: b0af b163 b201 25 The Crystal Tower 38:b236: b240 b2b4 b364 26 Let Me Know the Truth 38:b40b: b415 b4c9 b542 27 Forbidden Land 38:b585: b58f b5e6 b675 28 This is the Last Battle 3 38:b717: b721 b851 b927 b9ba ba66 29 The Dark Crystals 38:ba9a: baa4 bb02 bc22 2a Boss Battle 38:bcc0: bcca bd8b be98 bf61 bfb7 2b Parting with a Companion 39:8c8f: 8c99 8cb5 8cc9 2c Added Companion 39:8cd2: 8cdc 8cf6 8d13 2d Hazardous Short Music 2 39:8d1a: 8d24 8d57 8d84 2e Salonia 39:8da2: 8dac 8e21 8ed4 2f The Boundless Ocean 39:8f76: 8f80 8fe6 9043 30 Fall SFX 39:9128: 9132 913b 9140 31 Danger SFX 39:9148: 9152 916b 9182 918c 32 Shattering SFX 39:919c: 91a6 91b4 91b9 33 Applause SFX 39:91c3: 91cd 91da 91e5 91f0 34 Boo SFX 39:9201: 920b 9215 35 Bahamut Flies SFX 39:9220: 922a 922c 923d 36 Crystal Room 39:924a: 9254 92bd 37 The Everlasting World 2 39:b3b6: b3c0 b4c8 b6f9 38 Castle of Hain 39:b876: b880 b913 b9d8 39 Chocobo Forest 39:ba82: ba8c baa4 3a Let's Play the Piano Again! 39:babf: bac9 bae9 3b The Everlasting World 3 9:b40c: b416 b57d b991 bb7a 3c The Everlasting World 1 9:bcc7: bcd1 bd58 3d Hazardous Short Music 1 9:be7f: be89 beb4 bf00 3e This is the Last Battle 1 9:bf14: bf1e bf69 bf71 3f This is the Last Battle 2 9:bfa5: bfaf bf69 bf71 40 Let's Play the Piano! 9:bfb4: bfbe Moving Track Tables and Data This multi-bank system is implemented in a rather complex way across multiple functions and data structures, and changes to relocate the tables or track data will also be a bit complex. First, an array of 4 bytes gives the bank numbers to be loaded at $a000: 36:89bf ($6c9cf): $37, $38, $39, 9 The bank is switched to based on this table and the track number by the function at 36:899f ($6c9af): 899F LDA #$07 89A1 STA $8000 89A4 LDX #$00 89A6 LDA $7F43 89A9 CMP #$19 89AB BCC $89B8 89AD INX 89AE CMP #$2B 89B0 BCC $89B8 89B2 INX 89B3 CMP #$3B 89B5 BCC $89B8 89B7 INX 89B8 LDA $89BF,X 89BB STA $8001 89BE RTS Finally, the locations of the track tables are specified by the following code at 36:8a00 ($6CA10): 8A00 LDA $7F43 8A03 CMP #$19 8A05 BCC $8A0E 8A07 CMP #$2B 8A09 BCS $8A1D 8A0B SEC 8A0C SBC #$19 8A0E ASL A 8A0F TAX 8A10 LDA $A000,X 8A13 STA $D8 8A15 LDA $A001,X 8A18 STA $D9 8A1A JMP $8A57 8A1D CMP #$37 8A1F BCS $8A33 8A21 SEC 8A22 SBC #$2B 8A24 ASL A 8A25 TAX 8A26 LDA $8C77,X 8A29 STA $D8 8A2B LDA $8C78,X 8A2E STA $D9 8A30 JMP $8A57 8A33 CMP #$3B 8A35 BCS $8A49 8A37 SEC 8A38 SBC #$37 8A3A ASL A 8A3B TAX 8A3C LDA $B3AE,X 8A3F STA $D8 8A41 LDA $B3AF,X 8A44 STA $D9 8A46 JMP $8A57 8A49 SBC #$3B 8A4B ASL A 8A4C TAX 8A4D LDA $B400,X 8A50 STA $D8 8A52 LDA $B401,X 8A55 STA $D9 Sound Effect Table Sound Effect Table Address: 36:92c5 Unlike music, all sound effect data is stored in bank $36. Track Addr Sq2A Sq2FO NoiA NoiFO 0 (0) 9387: 938b (6d39b) 9393 (6d3a3) 1 (1) 939a: 939e (6d3ae) ffff 2 (2) 93a8: 93ac (6d3bc) 93b8 (6d3c8) 3 (3) 93c2: ffff 93c6 (6d3d6) 4 (4) 93cc: ffff 93d0 (6d3e0) 5 (5) 93df: 93e3 (6d3f3) ffff 6 (6) 93ec: 93f0 (6d400) ffff 7 (7) 93ff: 9403 (6d413) 940b (6d41b) 8 (8) 9411: 9415 (6d425) ffff 9 (9) 9420: 9424 (6d434) ffff 10 (a) 942c: ffff 9430 (6d440) 11 (b) 9440: 9444 (6d454) ffff 12 (c) 944f: 9453 (6d463) 945b (6d46b) 13 (d) 9463: 9467 (6d477) 9476 (6d486) 14 (e) 947f: 9483 (6d493) 949a (6d4aa) 15 (f) 94b3: 94b7 (6d4c7) 94be (6d4ce) 16 (10) 94c6: 94ca (6d4da) 94d3 (6d4e3) 17 (11) 94dd: ffff 94e1 (6d4f1) 18 (12) 94e7: ffff 94eb (6d4fb) 19 (13) 94f2: 94f6 (6d506) ffff 20 (14) 94fe: 9502 (6d512) ffff 21 (15) 950a: 950e (6d51e) 9517 (6d527) 22 (16) 9520: ffff 9524 (6d534) 23 (17) 952c: 9530 (6d540) ffff 24 (18) 953a: 953e (6d54e) ffff 25 (19) 9546: 954a (6d55a) ffff 26 (1a) 9550: 9554 (6d564) 955a (6d56a) 27 (1b) 9560: ffff 9564 (6d574) 28 (1c) 956b: 956f (6d57f) 9579 (6d589) 29 (1d) 957f: 9583 (6d593) ffff 30 (1e) 958e: 9592 (6d5a2) ffff 31 (1f) 9599: 959d (6d5ad) 95a8 (6d5b8) 32 (20) 95b4: 95b8 (6d5c8) ffff 33 (21) 95c3: 95c7 (6d5d7) 95cd (6d5dd) 34 (22) 95d7: ffff 95db (6d5eb) 35 (23) 95e6: ffff 95ea (6d5fa) 36 (24) 95f8: 95fc (6d60c) ffff 37 (25) 9604: ffff 9608 (6d618) 38 (26) 9612: ffff 9616 (6d626) 39 (27) 9620: ffff 9624 (6d634) 40 (28) 962f: ffff 9633 (6d643) 41 (29) 963e: 9642 (6d652) 964b (6d65b) 42 (2a) 9653: 9657 (6d667) 9662 (6d672) 43 (2b) 966c: ffff 9670 (6d680) 44 (2c) 967d: 9681 (6d691) 968e (6d69e) 45 (2d) 969c: 96a0 (6d6b0) ffff 46 (2e) 9387: 938b (6d39b) 9393 (6d3a3) 47 (2f) 96a8: 96ac (6d6bc) 96b4 (6d6c4) 48 (30) 96c5: ffff 96c9 (6d6d9) 49 (31) 96d4: 96d8 (6d6e8) 96eb (6d6fb) 50 (32) 96fc: ffff 9700 (6d710) 51 (33) 970e: ffff 9712 (6d722) 52 (34) 971e: 9722 (6d732) ffff 53 (35) 972e: 9732 (6d742) 973c (6d74c) 54 (36) 974a: 974e (6d75e) 9757 (6d767) 55 (37) 975e: 9762 (6d772) 976a (6d77a) 56 (38) 9779: 977d (6d78d) 978a (6d79a) 57 (39) 9795: ffff 9799 (6d7a9) 58 (3a) 9387: 938b (6d39b) 9393 (6d3a3) 59 (3b) 97a1: ffff 97a5 (6d7b5) 60 (3c) 97bf: 97c3 (6d7d3) 97d1 (6d7e1) 61 (3d) 97de: 97f8 (6d808) 97e2 (6d7f2) 62 (3e) 9806: 980a (6d81a) 9816 (6d826) 63 (3f) 981d: 9821 (6d831) ffff 64 (40) 9829: ffff 982d (6d83d) 65 (41) 9836: ffff 983a (6d84a) 66 (42) 9845: ffff 9849 (6d859) 67 (43) 985d: ffff 9861 (6d871) 68 (44) 986e: 9872 (6d882) 987b (6d88b) 69 (45) 9881: ffff 9885 (6d895) 70 (46) 988f: ffff 9893 (6d8a3) 71 (47) 989a: 989e (6d8ae) 98a5 (6d8b5) 72 (48) 98ab: 98af (6d8bf) 98b6 (6d8c6) 73 (49) 98bd: 98c1 (6d8d1) 98c7 (6d8d7) 74 (4a) 98d1: 98d5 (6d8e5) 98e0 (6d8f0) 75 (4b) 98e9: 98ed (6d8fd) ffff 76 (4c) 98f6: ffff 98fa (6d90a) 77 (4d) 9903: ffff 9907 (6d917) 78 (4e) 990f: ffff 9913 (6d923) 79 (4f) 9922: 9926 (6d936) 9930 (6d940) 80 (50) 9938: 993c (6d94c) 9947 (6d957) 81 (51) 9951: 9955 (6d965) 996b (6d97b) 82 (52) 9975: 9979 (6d989) 9982 (6d992) 83 (53) 9989: 998d (6d99d) 9999 (6d9a9) 84 (54) 99a3: 99a7 (6d9b7) ffff 85 (55) 99b2: ffff 99b6 (6d9c6) 86 (56) 99c3: ffff 99c7 (6d9d7) 87 (57) 99cd: 99d1 (6d9e1) 99d9 (6d9e9) 88 (58) 99e4: ffff 99e8 (6d9f8) 89 (59) 99f2: ffff 99f6 (6da06) 90 (5a) 96ba: ffff 96be (6d6ce) 91 (5b) 97ad: 97b1 (6d7c1) 97b9 (6d7c9) 92 (5c) 99ff: 9a03 (6da13) 9a1d (6da2d) 93 (5d) 9a3b: 9a3f (6da4f) ffff 94 (5e) 9a4e: 9a52 (6da62) ffff 95 (5f) 9a5b: 9a5f (6da6f) ffff 96 (60) 9a66: 9a6a (6da7a) ffff